/*
							Copyright (C) 2010  Alourien Team
									
						This file is part of the Alourien Engine.

    The Alourien Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    The Alourien Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with the Alourien Engine.  If not, see <http://www.gnu.org/licenses/>.
	
 *File Name: Alourien.cpp
 *Programmer: Manuel Cuellar
 *Date of Last Update: 9/30/2010
 *
 *Purpose: This is our main engine header's implementation. This is where 
 * creation of all API's occur, and where the actual rendering is done
 *
 *Basic info: PLEASE DO NOT CHANGE ANY CODE HERE WITHOUT PERMISSION AS THIS
 * AFFECTS MOSTLY EVERY OTHER PIECE OF CODE IN THE PROJECT
 *
 *UPDATES:
 *
 *
 *
     Copyright (C) 2010  Alourien Team
	 									
						This file is part of the Alourien Engine.

    The Alourien Engine is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    The Alourien Engine is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with the Alourien Engine.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "Alourien.h"
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>
#include <list>
#include "WinMain.h"

namespace Alourien
{
	Engine::Engine()
	{
		srand((unsigned int) time(NULL));
		p_maximizeProcessor = false;
		p_frameCount_core = 0;
		p_frameRate_core = 0;
		p_frameCount_real = 0;
		p_frameRate_real = 0;
		p_ambientColor = D3DCOLOR_RGBA(255,255,255,0);  //White light
		p_windowHandle = 0;
		p_pauseMode = false;
		p_versionMajor = VERSION_MAJOR;
		p_versionMinor = VERSION_MINOR;
		p_revision = REVISION;

		//Set default values
		this->setAppTitle("Alourien Engine");
		this->setScreenWidth(640);
		this->setScreenHeight(480);
		this->setColorDepth(32);
		this->setFullScreen(false);

		//Window handle must be set later for DirectX!
		this->setWindowHandle(0);
	}

	Engine::~Engine()
	{
		//Releases the DirectX objects
		if(this->p_device)
		{
			this->p_device->Release();
		}
		if(this->p_d3d)
		{
			this->p_d3d->Release();
		}
	}

	//This returns Current version data, which is set in the header
	std::string Engine::getVersionText()
	{
		std::ostringstream s;
		s << "Alourien Engine v" << p_versionMajor << "." << p_versionMinor
			<< "." << p_revision;
		return s.str();
	}
	
	//Handles any messages that need to be communicated to the user
	void Engine::message(std::string message, std::string title)
	{
		MessageBox(0, message.c_str(), title.c_str(), 0);
	}

	//Called whenever a fatal error occurs
	void Engine::fatalError(std::string message, std::string title)
	{
		this->message(message, title);
		Shutdown();
	}

	int Engine::Init(int width, int height, int colorDepth, bool fullscreen)
	{
		//Initialize Direct3D
		this->p_d3d = Direct3DCreate9(D3D_SDK_VERSION);
		if(this->p_d3d == NULL)
		{
			return 0;
		}

		//Get system desktop color depth
		D3DDISPLAYMODE dm;
		this->p_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &dm);

		//Set configurations for Direct3D
		D3DPRESENT_PARAMETERS d3dpp;
		ZeroMemory(&d3dpp, sizeof(d3dpp));
		d3dpp.Windowed = (!fullscreen);
		d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
		d3dpp.EnableAutoDepthStencil = TRUE;
		d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
		d3dpp.BackBufferFormat = dm.Format;
		d3dpp.BackBufferCount = 1;
		d3dpp.BackBufferWidth = width;
		d3dpp.BackBufferHeight = height;
		d3dpp.hDeviceWindow = p_windowHandle;

		//Create Direct3D device
		this->p_d3d->CreateDevice(
			D3DADAPTER_DEFAULT,
			D3DDEVTYPE_HAL,
			this->p_windowHandle,
			D3DCREATE_HARDWARE_VERTEXPROCESSING,
			&d3dpp,
			&this->p_device);

		if(this->p_device == NULL)
		{
			return 0;
		}

		//Clear the backbuffer to black
		this->ClearScene(D3DCOLOR_XRGB(0, 0, 0));


		//Create pointer to the backbuffer
		this->p_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO,		\
			&this->p_backbuffer);

		//Use ambient lighting and z-buffering
		this->p_device->SetRenderState(D3DRS_ZENABLE, TRUE);
		this->p_device->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
		this->SetAmbient(this->p_ambientColor);

		//Initialize 2D renderer
		HRESULT result = D3DXCreateSprite(this->p_device, &this->p_sprite_handler);
		if(result != D3D_OK)
		{
			return 0;
		}

		//Call game initialization external function
		if(!game_init(this->getWindowHandle()))
		{
			return 0;
		}

		//Set a default material
		SetDefaultMaterial();
		return 1;
	}

	void Engine::SetDefaultMaterial()
	{
		D3DMATERIAL9 mat;
		memset(&mat, 0, sizeof(mat));  //Allocates zero to all members of mat
		mat.Diffuse.r = 1.0f;
		mat.Diffuse.g = 1.0f;
		mat.Diffuse.b = 1.0f;
		mat.Diffuse.a = 1.0f;
		p_device->SetMaterial(&mat);
	}

	void Engine::ClearScene(D3DCOLOR color)
	{
		this->p_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,	\
			color, 1.0f, 0);
	}

	void Engine::SetAmbient(D3DCOLOR colorValue)
	{
		this->p_ambientColor = colorValue;
		this->p_device->SetRenderState(D3DRS_AMBIENT, this->p_ambientColor);
	}

	int Engine::RenderStart()
	{
		if(!this->p_device)
		{
			return 0;
		}
		if(this->p_device->BeginScene() != D3D_OK)
		{
			return 0;
		}
		return 1;
	}

	int Engine::RenderStop()
	{
		if(!this->p_device)
		{
			return 0;
		}
		if(this->p_device->EndScene() != D3D_OK)
		{
			return 0;
		}
		return 1;
	}

	void Engine::Shutdown()
	{
		gameover = true;
	}

	void Engine::Update()
	{
		static COurTimer timedUpdate;
		
		//Calculate core framerate
		++p_frameCount_core;
		if(p_coreTimer.stopwatch(999))
		{
			p_frameRate_core = p_frameCount_core;
			p_frameCount_core = 0;
		}

		//Fast update with no timing
		game_update();

		//Update with 60fps timing
		if(!timedUpdate.stopwatch(14))
		{
			if(!this->getMaximizeProcessor())
			{
				Sleep(1);
			}
		}
		else
		{
			//Calculate real framerate
			++p_frameCount_real;
			if(p_realTimer.stopwatch(999))
			{
				p_frameRate_real = p_frameCount_real;
				p_frameCount_real = 0;
			}
			
			//Begin rendering
			this->RenderStart();

			//Done Rendering
			this->RenderStop();
		}
	}

	void Engine::Close()
	{
		game_end();
	}

} //Namespace